package com.wiscamp.ninechapters.problems.infrastructure.repositories;

import com.wiscamp.ninechapters.problems.domain.models.types.ProblemType;
import com.wiscamp.ninechapters.problems.domain.repositories.ProblemTypeRepository;
import com.wiscamp.ninechapters.problems.infrastructure.converters.ProblemTypeConverter;
import com.wiscamp.ninechapters.problems.infrastructure.mappers.types.ProblemTypeMapper;
import com.wiscamp.ninechapters.problems.infrastructure.mappers.types.ProblemTypeViewMapper;
import com.wiscamp.ninechapters.problems.infrastructure.mappers.types.QuestionTypeViewMapper;
import com.wiscamp.ninechapters.problems.infrastructure.mappers.types.TypeRelationMapper;
import com.wiscamp.ninechapters.problems.infrastructure.po.types.ProblemTypePO;
import com.wiscamp.ninechapters.problems.infrastructure.po.types.TypeRelationPO;
import cube.ddd.aspect.HandleDataException;
import cube.ddd.domain.BaseRepository;
import cube.ddd.utils.PersistenceUtils;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import org.springframework.stereotype.Repository;

import java.util.Collection;
import java.util.Objects;

@Repository
public class ProblemTypeRepositoryImpl extends BaseRepository implements ProblemTypeRepository {
    private final ProblemTypeMapper problemTypeMapper;
    private final ProblemTypeViewMapper problemTypeViewMapper;
    private final TypeRelationMapper typeRelationMapper;
    private final QuestionTypeViewMapper questionTypeViewMapper;

    public ProblemTypeRepositoryImpl(ProblemTypeMapper problemTypeMapper, ProblemTypeViewMapper problemTypeViewMapper, TypeRelationMapper typeRelationMapper, QuestionTypeViewMapper questionTypeViewMapper) {
        this.problemTypeMapper = problemTypeMapper;
        this.problemTypeViewMapper = problemTypeViewMapper;
        this.typeRelationMapper = typeRelationMapper;
        this.questionTypeViewMapper = questionTypeViewMapper;
    }

    //region ProblemType
    private void loadDetails(ProblemType problemType) {
        if (Objects.nonNull(problemType)) {
            var questionTypeIds = typeRelationMapper.findQuestionTypeIdsByProblemType(problemType.getTypeId());
            problemType.setQuestionTypeIds(questionTypeIds);

            /*var questionTypeViews = typeRelationMapper.findQuestionTypesByProblemType(problemType.getTypeId());
            var questionTypes = CollectionUtils.toArrayList(questionTypeViews, QuestionTypeConverter.Instance::toQuestionTypeByView);
            problemType.setQuestionTypes(questionTypes);*/
        }
    }

    private ProblemTypePO getProblemTypePO(long typeId) {
        return PersistenceUtils.findById(typeId, problemTypeMapper::findById);
    }

    /**
     * Get a problem type
     *
     * @param typeId
     * @return
     */
    @HandleDataException
    public ProblemType getProblemType(long typeId) {
        var problemType = PersistenceUtils.findEntityById(typeId, problemTypeViewMapper::findById,
                ProblemTypeConverter.Instance::toProblemTypeByView);
        loadDetails(problemType);
        return problemType;
    }

    /**
     * Get a problem type
     *
     * @param typeCode
     * @return
     */
    @HandleDataException
    public ProblemType getProblemTypeByCode(@NotBlank String typeCode) {
        var problemType = PersistenceUtils.getEntityByText(typeCode, problemTypeViewMapper::findByTypeCode,
                ProblemTypeConverter.Instance::toProblemTypeByView);
        loadDetails(problemType);
        return problemType;
    }

    @HandleDataException
    public Collection<ProblemType> getProblemTypesByIds(@NotEmpty Collection<Long> ids) {
        var problemTypes = PersistenceUtils.getEntitiesByIds(ids, problemTypeViewMapper::findAllById,
                ProblemTypeConverter.Instance::toProblemTypeByView);
        for (var problemType : problemTypes) {
            loadDetails(problemType);
        }
        return problemTypes;
    }


    /**
     * Gets a item types
     *
     * @param typeName
     * @return
     */
    @HandleDataException
    public Collection<ProblemType> getProblemTypesByName(@NotBlank String typeName) {
        var problemTypes = PersistenceUtils.getEntitiesByText(typeName, problemTypeViewMapper::findListByTypeName,
                ProblemTypeConverter.Instance::toProblemTypeByView);
        for (var problemType : problemTypes) {
            loadDetails(problemType);
        }
        return problemTypes;
    }

    @HandleDataException
    public Collection<ProblemType> getAvailableProblemTypes() {
        var problemTypes = PersistenceUtils.getEntities(problemTypeViewMapper::findListByAvailable,
                ProblemTypeConverter.Instance::toProblemTypeByView);

        for (var problemType : problemTypes) {
            loadDetails(problemType);
        }
        return problemTypes;
    }

    /**
     * Create a problem type
     *
     * @param problemType
     * @return
     */
    @HandleDataException
    public ProblemType saveProblemType(@NotNull ProblemType problemType) {
        var newId = problemType.getTypeId();
        if (problemType.getTypeId() <= 0) {
            var newType = PersistenceUtils.createEntity(problemType, problemTypeMapper::saveAndFlush,
                    ProblemTypeConverter.Instance::toProblemTypePO, ProblemTypeConverter.Instance::toProblemType);
            newId = newType.getTypeId();
        } else {
            var existedPO = getProblemTypePO(problemType.getTypeId());
            if (existedPO == null) existedPO = new ProblemTypePO();
            PersistenceUtils.updateEntity(problemType, existedPO, problemTypeMapper::saveAndFlush,
                    ProblemTypeConverter.Instance::toProblemTypePO, ProblemTypeConverter.Instance::toProblemType);
        }
        return getProblemType(newId);
    }

    //endregion

    //region TypeRelation
    private TypeRelationPO getTypeRelationPO(long problemTypeId, long questionTypeId) {
        return typeRelationMapper.findTypeRelation(problemTypeId, questionTypeId);
    }

    @HandleDataException
    public boolean bindQuestionType(long problemTypeId, long questionTypeId) {
        var typeRelationPO = getTypeRelationPO(problemTypeId, questionTypeId);
        if (typeRelationPO == null) {
            typeRelationPO = new TypeRelationPO(problemTypeId, questionTypeId);
            typeRelationMapper.saveAndFlush(typeRelationPO);
        }
        return true;
    }

    @HandleDataException
    public boolean unbindQuestionType(long problemTypeId, long questionTypeId) {
        typeRelationMapper.deleteTypeRelation(problemTypeId, questionTypeId);
        return true;
    }
    //endregion
}
